import requests
import json
import pandas as pd
from datetime import datetime
from dateutil.relativedelta import relativedelta as timedelta
import argparse
from config import API_CONFIG
# 从config.py导入必要的函数和配置
from config import get_default_headers, switch_token, get_active_token, update_access_token

def print_config_info():
    """
    打印当前配置信息
    """
    print(f"access-token: {get_active_token()[:10]}****{get_active_token()[-4:]}")



def get_item_list(task_id, node_id, package_ids, page_num=1, page_size=50, headers=None):
    """
    调用get-item-list接口获取项目列表并返回特定字段
    
    Args:
        task_id (str): 任务ID
        node_id (str): 节点ID
        package_ids (str or list): 批次ID，可以是单个ID或ID列表
        page_num (int): 页码，默认为1
        page_size (int): 每页数量，默认为50
        headers (dict, optional): 自定义请求头，如果为None则使用默认请求头
    
    Returns:
        dict: 包含状态信息和数据的字典
    """
    # 构建请求URL - 更新为新的域名
    url = "https://app.abaka.ai/api/v2/item/get-item-list"
    
    # 确保package_ids是列表格式
    if isinstance(package_ids, str):
        package_ids = [package_ids]
    
    # 如果未提供自定义请求头，使用默认请求头
    if headers is None:
        # 使用第一个package_id作为batch_id
        referer_batch_id = package_ids[0] if package_ids else "68f1e89b8fc939d1af1b726f"
        # 不传递item_id，因为这是列表请求
        headers = get_default_headers(task_id=task_id, batch_id=referer_batch_id)
    
    data = {
        "taskId": task_id,
        "pageNum": page_num,
        "pageSize": page_size,
        "search": {
            "type": "AND",
            "units": []
        },
        "packageIds": package_ids,
        "nodeId": node_id
    }
    
    try:
        print("\n正在获取项目列表...")
        # 打印请求参数（隐藏敏感信息）
        masked_headers = headers.copy()
        if 'access-token' in masked_headers:
            token = masked_headers['access-token']
            masked_headers['access-token'] = f"{token[:10]}****{token[-4:]}"
        print(f"请求URL: {url}")
        print(f"请求参数: taskId={task_id}, nodeId={node_id}, packageIds={package_ids}")
        print(f"请求头(部分): {dict(list(masked_headers.items())[:3])}")
        
        # 添加verify=False以忽略SSL验证
        response = requests.post(url, headers=headers, json=data, timeout=60, verify=False)
        status_code = response.status_code
        print(f"请求状态码: {status_code}")
        
        # 无论状态码如何，都尝试解析响应内容
        try:
            response_data = response.json()
            print(f"响应内容(部分): {json.dumps(response_data)[:200]}...")
        except json.JSONDecodeError:
            response_data = None
            print(f"响应内容(非JSON): {response.text[:200]}...")
        
        # 如果返回401错误，尝试切换token并重新请求
        if status_code == 401:
            print("\n[错误] 认证失败 (401 Unauthorized)")
            current_token = get_active_token()
            # 安全地显示token信息
            token_display = f"{current_token[:10]}****{current_token[-4:]}" if current_token else "[空]"
            print(f"当前使用的token: {token_display}")
            
            # 只有当备用token存在、非空且与当前token不同时才尝试切换
            alt_token = API_CONFIG.get("alternative_token")
            if alt_token and alt_token.strip() and alt_token != current_token:
                print("尝试切换到备用token...")
                switch_token("alternative_token")
                # 使用新token重新构建请求头
                headers = get_default_headers(task_id=task_id, batch_id=referer_batch_id, item_id=None)
                print("使用新token重新请求...")
                response = requests.post(url, headers=headers, json=data, timeout=60, verify=False)
                status_code = response.status_code
                print(f"新请求状态码: {status_code}")
            else:
                print("[警告] 备用token不存在、为空或与当前token相同，无法切换")
                print("请通过以下方式提供有效的token:")
                print("1. 使用命令行参数: python combine_api_calls.py --access-token YOUR_VALID_TOKEN")
                print("2. 或在交互式模式中输入有效的token")
                print("注意：确保token格式正确且未过期")
                # 可以选择退出或继续使用当前token
                # sys.exit(1)  # 如果需要直接退出
            try:
                response_data = response.json()
                print(f"新响应内容(部分): {json.dumps(response_data)[:200]}...")
            except json.JSONDecodeError:
                response_data = None
                print(f"新响应内容(非JSON): {response.text[:200]}...")
        
        # 处理500错误
        elif status_code == 500:
            print("\n[错误] 服务器内部错误 (500)")
            print("尝试使用简化的请求参数...")
            # 使用默认批次ID
            test_package_ids = ["68d28091dd785cd49b388c61"]  # 默认批次ID
            test_data = {
                "taskId": task_id,
                "pageNum": 1,
                "pageSize": 10,  # 减少页面大小
                "packageIds": test_package_ids,
                "nodeId": node_id
            }
            print(f"使用测试参数: packageIds={test_package_ids}, pageSize=10")
            
            # 重新发送请求
            test_response = requests.post(url, headers=headers, json=test_data, timeout=60, verify=False)
            test_status_code = test_response.status_code
            print(f"测试请求状态码: {test_status_code}")
            
            try:
                response_data = test_response.json()
                print(f"测试响应内容(部分): {json.dumps(response_data)[:200]}...")
            except json.JSONDecodeError:
                response_data = None
                print(f"测试响应内容(非JSON): {test_response.text[:200]}...")
            
            status_code = test_status_code
        
        result = {
            "success": False,
            "message": "",
            "total": 0,
            "data": [],
            "status_code": status_code
        }
        
        if response_data and 'data' in response_data and isinstance(response_data['data'], dict):
            total = response_data['data'].get('total', 0)
            result["total"] = total
            
            if 'data' in response_data['data'] and isinstance(response_data['data']['data'], list):
                items = response_data['data']['data']
                extracted_items = []
                
                for item in items:
                    # 提取processStatus和checkRound来判断标注状态
                    process_status = item.get('processStatus', None)
                    if process_status is not None and isinstance(process_status, dict):
                        # 结合checkRound和label字段来判断标注状态
                        has_check_round = 'checkRound' in process_status
                        label_value = process_status.get('label', '')
                        
                        # 如果label字段为LABELED且有checkRound，则为已标注
                        # 否则为标注中
                        if label_value == 'LABELED' and has_check_round:
                            annotation_status = '已标注'
                        else:
                            annotation_status = '标注中'
                    else:
                        # 如果没有processStatus对象，默认为标注中
                        annotation_status = '标注中'
                    
                    # 获取operateTime或createdAt，只保留日期部分
                    time_value = item.get('operateTime', item.get('createdAt', 'N/A'))
                    # 如果时间值不为N/A，尝试提取日期部分
                    if time_value != 'N/A':
                        # 确保时间值是字符串
                        time_str = str(time_value)
                        # 处理不同格式的时间字符串
                        if ' ' in time_str:  # 处理包含空格的格式，如"2023-10-15 12:34:56"
                            formatted_date = time_str.split(' ')[0]
                        elif 'T' in time_str:  # 处理ISO格式，如"2023-10-15T12:34:56Z"
                            formatted_date = time_str.split('T')[0]
                        elif len(time_str) >= 10:  # 处理其他可能的格式，只取前10个字符
                            formatted_date = time_str[:10]
                        else:
                            formatted_date = time_str
                    else:
                        formatted_date = 'N/A'
                    
                    extracted_items.append({
                        "_id": item.get('_id', 'N/A'),
                        "domainId": item.get('domainId', 'N/A'),
                        "operateTime": formatted_date,  # 只保留日期部分
                        "annotationStatus": annotation_status  # 添加标注状态
                    })
                
                result["data"] = extracted_items
                result["success"] = True
                result["message"] = f"成功获取到 {len(extracted_items)} 条记录"
            
        return result
            
    except Exception as e:
        print(f"获取项目列表失败: {str(e)}")
        import traceback
        print(f"错误详情: {traceback.format_exc()}")
        return {
            "success": False,
            "message": str(e),
            "total": 0,
            "data": [],
            "status_code": 0
        }

def get_item_skip_list(task_id, node_id, package_ids, page_num=1, page_size=50, headers=None):
    """
    调用get-item-skip-list接口获取被跳过的项目列表
    
    Args:
        task_id (str): 任务ID
        node_id (str): 节点ID
        package_ids (str or list): 批次ID，可以是单个ID或ID列表
        page_num (int): 页码，默认为1
        page_size (int): 每页数量，默认为50
        headers (dict, optional): 自定义请求头，如果为None则使用默认请求头
    
    Returns:
        dict: 包含状态信息和数据的字典
    """
    # 构建请求URL
    url = "https://app.abaka.ai/api/v2/item/get-item-skip-list"
    
    # 确保package_ids是列表格式
    if isinstance(package_ids, str):
        package_ids = [package_ids]
    
    # 如果未提供自定义请求头，使用默认请求头
    if headers is None:
        referer_batch_id = package_ids[0] if package_ids else "68d28091dd785cd49b388c61"
        headers = get_default_headers(task_id=task_id, batch_id=referer_batch_id)
    
    data = {
        "taskId": task_id,
        "pageNum": page_num,
        "pageSize": page_size,
        "search": {
            "type": "AND",
            "units": []
        },
        "packageIds": package_ids,
        "nodeId": node_id
    }
    
    try:
        print("\n正在获取被跳过项目列表...")
        # 打印请求参数（隐藏敏感信息）
        masked_headers = headers.copy()
        if 'access-token' in masked_headers:
            token = masked_headers['access-token']
            masked_headers['access-token'] = f"{token[:10]}****{token[-4:]}"
        print(f"请求URL: {url}")
        print(f"请求参数: taskId={task_id}, nodeId={node_id}, packageIds={package_ids}")
        print(f"请求头(部分): {dict(list(masked_headers.items())[:3])}")
        
        response = requests.post(url, headers=headers, json=data, timeout=60)
        status_code = response.status_code
        print(f"请求状态码: {status_code}")
        
        # 无论状态码如何，都尝试解析响应内容
        try:
            response_data = response.json()
            print(f"响应内容(部分): {json.dumps(response_data)[:200]}...")
        except json.JSONDecodeError:
            response_data = None
            print(f"响应内容(非JSON): {response.text[:200]}...")
        
        # 如果返回401错误，尝试切换token并重新请求
        if status_code == 401:
            print("\n[错误] 认证失败 (401 Unauthorized)")
            print("尝试切换到备用token...")
            switch_token("alternative_token")
            # 使用新token重新构建请求头
            headers = get_default_headers(task_id=task_id, batch_id=referer_batch_id)
            print("使用新token重新请求...")
            response = requests.post(url, headers=headers, json=data, timeout=60)
            status_code = response.status_code
            print(f"新请求状态码: {status_code}")
            try:
                response_data = response.json()
                print(f"新响应内容(部分): {json.dumps(response_data)[:200]}...")
            except json.JSONDecodeError:
                response_data = None
                print(f"新响应内容(非JSON): {response.text[:200]}...")
        
        # 处理500错误
        elif status_code == 500:
            print("\n[错误] 服务器内部错误 (500)")
            print("尝试使用简化的请求参数...")
            # 使用默认批次ID
            test_package_ids = ["68d28091dd785cd49b388c61"]  # 默认批次ID
            test_data = {
                "taskId": task_id,
                "pageNum": 1,
                "pageSize": 10,  # 减少页面大小
                "packageIds": test_package_ids,
                "nodeId": node_id
            }
            print(f"使用测试参数: packageIds={test_package_ids}, pageSize=10")
            
            # 重新发送请求
            test_response = requests.post(url, headers=headers, json=test_data, timeout=60)
            test_status_code = test_response.status_code
            print(f"测试请求状态码: {test_status_code}")
            
            try:
                response_data = test_response.json()
                print(f"测试响应内容(部分): {json.dumps(response_data)[:200]}...")
            except json.JSONDecodeError:
                response_data = None
                print(f"测试响应内容(非JSON): {test_response.text[:200]}...")
            
            status_code = test_status_code
        
        result = {
            "success": False,
            "message": "",
            "total": 0,
            "data": [],
            "status_code": status_code
        }
        
        if response_data and 'data' in response_data and isinstance(response_data['data'], dict):
            total = response_data['data'].get('total', 0)
            result["total"] = total
            
            if 'data' in response_data['data'] and isinstance(response_data['data']['data'], list):
                items = response_data['data']['data']
                extracted_items = []
                
                for item in items:
                    # 跳过的项目直接设置状态为"已跳过"
                    # 获取operateTime或createdAt，只保留日期部分
                    time_value = item.get('operateTime', item.get('createdAt', 'N/A'))
                    # 如果时间值不为N/A，尝试提取日期部分
                    if time_value != 'N/A':
                        # 确保时间值是字符串
                        time_str = str(time_value)
                        # 处理不同格式的时间字符串
                        if ' ' in time_str:  # 处理包含空格的格式，如"2023-10-15 12:34:56"
                            formatted_date = time_str.split(' ')[0]
                        elif 'T' in time_str:  # 处理ISO格式，如"2023-10-15T12:34:56Z"
                            formatted_date = time_str.split('T')[0]
                        elif len(time_str) >= 10:  # 处理其他可能的格式，只取前10个字符
                            formatted_date = time_str[:10]
                        else:
                            formatted_date = time_str
                    else:
                        formatted_date = 'N/A'
                    
                    extracted_items.append({
                        "_id": item.get('_id', 'N/A'),
                        "domainId": item.get('domainId', 'N/A'),
                        "operateTime": formatted_date,  # 只保留日期部分
                        "annotationStatus": "已跳过"  # 跳过的项目直接设置为"已跳过"
                    })
                
                result["data"] = extracted_items
                result["success"] = True
                result["message"] = f"成功获取到 {len(extracted_items)} 条被跳过的项目记录"
            
        return result
            
    except Exception as e:
        print(f"获取被跳过项目列表失败: {str(e)}")
        import traceback
        print(f"错误详情: {traceback.format_exc()}")
        return {
            "success": False,
            "message": str(e),
            "total": 0,
            "data": [],
            "status_code": 0
        }

def get_find_labels(task_id, item_id, headers=None):
    """
    调用find-labels接口获取项目的标签信息，提取Modified instruction_1、Modified instruction_2和Skip or not的数据
    
    Args:
        task_id (str): 任务ID
        item_id (str): 项目ID
        headers (dict, optional): 自定义请求头
    
    Returns:
        dict: 格式为{item_id: {"modified_instruction_1": value1, "modified_instruction_2": value2, "skip_or_not": value3}}的字典
    """
    url = "https://app.abaka.ai/api/v2/label/find-labels"
    
    # 如果未提供自定义请求头，使用默认请求头
    if headers is None:
        # 确保headers包含必要的认证信息
        headers = get_default_headers(task_id=task_id, batch_id=item_id, item_id=item_id)
    
    # 构建请求数据
    data = {
        "itemId": item_id,
        "taskId": task_id
    }
    
    # 初始化结果字典，包含三个字段
    result = {item_id: {
        "modified_instruction_1": "-",
        "modified_instruction_2": "-",
        "skip_or_not": "-"
    }}
    
    # 标记是否已找到两种hash模式
    found_count = 0
    
    try:
        print(f"\n正在获取项目 {item_id} 的标签信息...")
        # 打印请求参数（隐藏敏感信息）
        masked_headers = headers.copy()
        if 'access-token' in masked_headers:
            token = masked_headers['access-token']
            masked_headers['access-token'] = f"{token[:10]}****{token[-4:]}"
        print(f"请求URL: {url}")
        print(f"请求参数: taskId={task_id}, itemId={item_id}")
        print(f"请求头(部分): {dict(list(masked_headers.items())[:3])}")
        
        # 添加verify=False以忽略SSL验证
        response = requests.post(url, headers=headers, json=data, timeout=30, verify=False)
        print(f"响应状态码: {response.status_code}")
        
        # 如果返回401错误，尝试切换token并重新请求
        if response.status_code == 401:
            print("\n[错误] 认证失败 (401 Unauthorized)")
            print("尝试切换到备用token...")
            switch_token("alternative_token")
            # 使用新token重新构建请求头
            headers = get_default_headers(task_id=task_id, batch_id=item_id, item_id=item_id)
            print("使用新token重新请求...")
            response = requests.post(url, headers=headers, json=data, timeout=30, verify=False)
            print(f"新请求状态码: {response.status_code}")
        
        # 尝试解析响应内容
        try:
            response_data = response.json()
            print(f"响应内容(部分): {json.dumps(response_data)[:200]}...")
        except json.JSONDecodeError:
            print(f"响应内容不是有效的JSON: {response.text[:200]}...")
            return result
        
        # 处理响应数据 - 根据用户提供的JSON结构进行处理
        # 首先检查是否有data字段，这是最常见的响应格式
        if isinstance(response_data, dict) and 'data' in response_data:
            data_list = response_data['data']
            if isinstance(data_list, list):
                print(f"处理data列表，共{len(data_list)}个元素")
                for item in data_list:
                    # 确保item是字典类型
                    if isinstance(item, dict):
                        # 直接访问item中的data字段（根据用户提供的JSON结构）
                        if 'data' in item and isinstance(item['data'], dict):
                            item_data = item['data']
                            # 检查hash和value字段
                            if 'hash' in item_data and 'value' in item_data:
                                hash_value = item_data['hash']
                                value = item_data['value']
                                print(f"处理记录: hash={hash_value}, value={value}")
                                
                                # 精确匹配用户提供的hash值格式
                                if hash_value == "Modified instruction_1. Modified instruction":
                                    result[item_id]["modified_instruction_1"] = value
                                    print(f"找到Modified instruction_1: {value}")
                                    found_count += 1
                                elif hash_value == "Modified instruction_2. Modified instruction":
                                    result[item_id]["modified_instruction_2"] = value
                                    print(f"找到Modified instruction_2: {value}")
                                    found_count += 1
                                # 检查是否是Skip or not标签
                                elif 'data' in item and isinstance(item['data'], dict) and 'label' in item['data']:
                                    label = item['data']['label']
                                    if label == "check_Skip or not" and 'value' in item['data']:
                                        result[item_id]["skip_or_not"] = item['data']['value']
                                        print(f"找到Skip or not: {item['data']['value']}")
                                
                                # 如果两种hash都找到了，可以提前结束循环
                                if found_count >= 2:
                                    print("已找到所有需要的hash值，提前结束循环")
                                    break
        # 也处理直接返回列表的情况
        elif isinstance(response_data, list):
            print(f"处理直接返回的列表，共{len(response_data)}个元素")
            for item in response_data:
                if isinstance(item, dict) and 'data' in item and isinstance(item['data'], dict):
                    item_data = item['data']
                    if 'hash' in item_data and 'value' in item_data:
                        hash_value = item_data['hash']
                        value = item_data['value']
                        print(f"处理记录: hash={hash_value}, value={value}")
                        
                        if hash_value == "Modified instruction_1. Modified instruction":
                            result[item_id]["modified_instruction_1"] = value
                            print(f"找到Modified instruction_1: {value}")
                            found_count += 1
                        elif hash_value == "Modified instruction_2. Modified instruction":
                            result[item_id]["modified_instruction_2"] = value
                            print(f"找到Modified instruction_2: {value}")
                            found_count += 1
                        # 检查是否是Skip or not标签
                        elif 'data' in item and isinstance(item['data'], dict) and 'label' in item['data']:
                            label = item['data']['label']
                            if label == "check_Skip or not" and 'value' in item['data']:
                                result[item_id]["skip_or_not"] = item['data']['value']
                                print(f"找到Skip or not: {item['data']['value']}")
                        
                        if found_count >= 2:
                            print("已找到所有需要的hash值，提前结束循环")
                            break
        else:
            print("响应数据格式不符合预期")
            # 打印响应结构以便调试
            if isinstance(response_data, dict):
                print(f"响应字典的键: {list(response_data.keys())}")
    
    except Exception as e:
        print(f"获取标签信息失败: {str(e)}")
        import traceback
        print(f"错误详情: {traceback.format_exc()}")
    
    print(f"项目 {item_id} 处理完成")
    return result

def get_find_labels_batch(task_id, item_ids, headers=None):
    """
    批量调用find-labels接口获取多个项目的Modified instruction_1和Modified instruction_2数据
    
    Args:
        task_id (str): 任务ID
        item_ids (list): 项目ID列表
        headers (dict, optional): 自定义请求头
    
    Returns:
        dict: 所有项目的标签数据，格式为{item_id: {"modified_instruction_1": value1, "modified_instruction_2": value2}}
    """
    all_labels = {}
    
    print(f"\n===== 获取{len(item_ids)}个项目的标签信息 =====")
    
    # 逐个调用find-labels接口
    for i, item_id in enumerate(item_ids, 1):
        print(f"\n[{i}/{len(item_ids)}]")
        label_info = get_find_labels(task_id, item_id, headers)
        if label_info:
            all_labels.update(label_info)
    
    print(f"\n成功获取{len(all_labels)}个项目的标签信息")
    return all_labels

def get_item_abandon_list(task_id, node_id, package_ids, page_num=1, page_size=50, headers=None):
    """
    调用get-item-abandon-list接口获取被放弃的项目列表
    
    Args:
        task_id (str): 任务ID
        node_id (str): 节点ID
        package_ids (str or list): 批次ID，可以是单个ID或ID列表
        page_num (int): 页码，默认为1
        page_size (int): 每页数量，默认为50
        headers (dict, optional): 自定义请求头，如果为None则使用默认请求头
    
    Returns:
        dict: 包含状态信息和数据的字典
    """
    # 构建请求URL
    url = "https://app.abaka.ai/api/v2/item/get-item-abandon-list"
    
    # 确保package_ids是列表格式
    if isinstance(package_ids, str):
        package_ids = [package_ids]
    
    # 如果未提供自定义请求头，使用默认请求头
    if headers is None:
        referer_batch_id = package_ids[0] if package_ids else "68d28091dd785cd49b388c61"
        headers = get_default_headers(task_id=task_id, batch_id=referer_batch_id)
    
    data = {
        "taskId": task_id,
        "pageNum": page_num,
        "pageSize": page_size,
        "search": {
            "type": "AND",
            "units": []
        },
        "packageIds": package_ids,
        "nodeId": node_id
    }
    
    try:
        print("\n正在获取被放弃项目列表...")
        # 打印请求参数（隐藏敏感信息）
        masked_headers = headers.copy()
        if 'access-token' in masked_headers:
            token = masked_headers['access-token']
            masked_headers['access-token'] = f"{token[:10]}****{token[-4:]}"
        print(f"请求URL: {url}")
        print(f"请求参数: taskId={task_id}, nodeId={node_id}, packageIds={package_ids}")
        print(f"请求头(部分): {dict(list(masked_headers.items())[:3])}")
        
        response = requests.post(url, headers=headers, json=data, timeout=60)
        status_code = response.status_code
        print(f"请求状态码: {status_code}")
        
        # 无论状态码如何，都尝试解析响应内容
        try:
            response_data = response.json()
            print(f"响应内容(部分): {json.dumps(response_data)[:200]}...")
        except json.JSONDecodeError:
            response_data = None
            print(f"响应内容(非JSON): {response.text[:200]}...")
        
        # 如果返回401错误，尝试切换token并重新请求
        if status_code == 401:
            print("\n[错误] 认证失败 (401 Unauthorized)")
            print("尝试切换到备用token...")
            switch_token("alternative_token")
            # 使用新token重新构建请求头
            headers = get_default_headers(task_id=task_id, batch_id=referer_batch_id)
            print("使用新token重新请求...")
            response = requests.post(url, headers=headers, json=data, timeout=60)
            status_code = response.status_code
            print(f"新请求状态码: {status_code}")
            try:
                response_data = response.json()
                print(f"新响应内容(部分): {json.dumps(response_data)[:200]}...")
            except json.JSONDecodeError:
                response_data = None
                print(f"新响应内容(非JSON): {response.text[:200]}...")
        
        # 处理500错误
        elif status_code == 500:
            print("\n[错误] 服务器内部错误 (500)")
            print("尝试使用简化的请求参数...")
            # 使用默认批次ID
            test_package_ids = ["68d28091dd785cd49b388c61"]  # 默认批次ID
            test_data = {
                "taskId": task_id,
                "pageNum": 1,
                "pageSize": 10,  # 减少页面大小
                "packageIds": test_package_ids,
                "nodeId": node_id
            }
            print(f"使用测试参数: packageIds={test_package_ids}, pageSize=10")
            
            # 重新发送请求
            test_response = requests.post(url, headers=headers, json=test_data, timeout=60)
            test_status_code = test_response.status_code
            print(f"测试请求状态码: {test_status_code}")
            
            try:
                response_data = test_response.json()
                print(f"测试响应内容(部分): {json.dumps(response_data)[:200]}...")
            except json.JSONDecodeError:
                response_data = None
                print(f"测试响应内容(非JSON): {test_response.text[:200]}...")
            
            status_code = test_status_code
        
        result = {
            "success": False,
            "message": "",
            "total": 0,
            "data": [],
            "status_code": status_code
        }
        
        if response_data and 'data' in response_data and isinstance(response_data['data'], dict):
            total = response_data['data'].get('total', 0)
            result["total"] = total
            
            if 'data' in response_data['data'] and isinstance(response_data['data']['data'], list):
                items = response_data['data']['data']
                extracted_items = []
                
                for item in items:
                    # 被放弃的项目直接设置状态为"已放弃"
                    extracted_items.append({
                        "_id": item.get('_id', 'N/A'),
                        "domainId": item.get('domainId', 'N/A'),
                        "operateTime": item.get('operateTime', item.get('createdAt', 'N/A')),  # 使用operateTime，不存在时使用createdAt作为备选
                        "annotationStatus": "已放弃"  # 被放弃的项目直接设置为"已放弃"
                    })
                
                result["data"] = extracted_items
                result["success"] = True
                result["message"] = f"成功获取到 {len(extracted_items)} 条被放弃的项目记录"
            
        return result
            
    except Exception as e:
        print(f"获取被放弃项目列表失败: {str(e)}")
        import traceback
        print(f"错误详情: {traceback.format_exc()}")
        return {
            "success": False,
            "message": str(e),
            "total": 0,
            "data": [],
            "status_code": 0
        }


def get_find_labels_batch(task_id, item_ids, headers=None):
    """
    批量调用find-labels接口获取多个项目的Modified instruction和Skip or not数据
    
    Args:
        task_id (str): 任务ID
        item_ids (list): 项目ID列表
        headers (dict, optional): 自定义请求头
    
    Returns:
        dict: 所有项目的数据，格式为{item_id: {"modified_instruction_1": value1, "modified_instruction_2": value2, "skip_or_not": value3}}
    """
    all_labels = {}
    
    print(f"\n===== 获取{len(item_ids)}个项目的标签信息 =====")
    
    # 逐个调用find-labels接口
    for i, item_id in enumerate(item_ids, 1):
        print(f"\n[{i}/{len(item_ids)}]")
        label_info = get_find_labels(task_id, item_id, headers)
        if label_info:
            all_labels.update(label_info)
    
    print(f"\n成功获取{len(all_labels)}个项目的标签信息")
    return all_labels

def get_task_info(task_id, headers=None):
    """
    调用get-task-info接口获取任务信息，提取Skip or not和Applicable的配置值
    
    Args:
        task_id (str): 任务ID
        headers (dict, optional): 自定义请求头
    
    Returns:
        tuple: (skip_or_not_value, applicable_value)
    """
    url = "https://app.abaka.ai/api/v2/task/get/task-info"
    
    if headers is None:
        headers = get_default_headers(task_id=task_id)
    
    # 添加特殊的请求头
    headers['content-type'] = 'application/json'
    headers['x-timezone'] = 'Asia/Shanghai'
    
    # 打印请求头信息（隐藏敏感信息）
    masked_headers = headers.copy()
    if 'access-token' in masked_headers:
        token = masked_headers['access-token']
        masked_headers['access-token'] = f"{token[:10]}****{token[-4:]}"
    print(f"\nget_task_info - 请求头(部分): {dict(list(masked_headers.items())[:3])}")
    print(f"请求参数: taskId={task_id}")
    
    payload = {
        "taskId": task_id
    }
    
    try:
        print(f"\n正在获取任务 {task_id} 的信息，提取Skip or not配置...")
        # 添加verify=False以忽略SSL验证
        response = requests.post(url, headers=headers, json=payload, timeout=30, verify=False)
        print(f"响应状态码: {response.status_code}")
        
        # 如果返回401错误，尝试切换token并重新请求
        if response.status_code == 401:
            print("\n[错误] 认证失败 (401 Unauthorized)")
            print("尝试切换到备用token...")
            switch_token("alternative_token")
            # 使用新token重新构建请求头
            headers = get_default_headers(task_id=task_id)
            headers['content-type'] = 'application/json'
            headers['x-timezone'] = 'Asia/Shanghai'
            print("使用新token重新请求...")
            # 添加verify=False以忽略SSL验证
            response = requests.post(url, headers=headers, json=payload, timeout=30, verify=False)
            print(f"新请求状态码: {response.status_code}")
        
        # 尝试解析响应内容
        try:
            response_data = response.json()
            print(f"响应内容(部分): {json.dumps(response_data)[:200]}...")
            
            # 打印详细的响应结构，以便用户查找Skip or not的位置
            print("\n详细响应结构分析：")
            print(f"1. 响应根结构: {list(response_data.keys())}")
            
            if 'data' in response_data:
                data = response_data['data']
                print(f"2. data类型: {type(data).__name__}")
                
                if isinstance(data, dict):
                    print(f"3. data字典键: {list(data.keys())}")
                    
                    if 'setting' in data:
                        setting = data['setting']
                        print(f"4. setting类型: {type(setting).__name__}")
                        
                        if isinstance(setting, dict):
                            print(f"5. setting字典键: {list(setting.keys())}")
                            
                            if 'labelConfig' in setting:
                                label_config = setting['labelConfig']
                                print(f"6. labelConfig类型: {type(label_config).__name__}")
                                
                                # 特别处理labelConfig是列表的情况
                                if isinstance(label_config, list):
                                    print(f"7. labelConfig列表长度: {len(label_config)}")
                                    print(f"\n=== labelConfig列表内容预览 ===")
                                    
                                    # 打印前3个列表项的内容
                                    for i, item in enumerate(label_config[:3], 1):
                                        print(f"\n列表项 {i} (类型: {type(item).__name__}):")
                                        if isinstance(item, dict):
                                            print(f"  - 字典键: {list(item.keys())}")
                                            # 打印更详细的信息
                                            for key, value in item.items():
                                                if isinstance(value, (dict, list)):
                                                    print(f"  - {key}: {type(value).__name__}")
                                                else:
                                                    print(f"  - {key}: {value}")
                                        
                                        # 在列表项中查找可能包含Skip or not的字段
                                        if isinstance(item, dict):
                                            # 检查item中是否有label字段等于Skip or not
                                            if item.get('label') == 'Skip or not':
                                                print(f"  \n  找到Skip or not! 在列表项 {i}")
                                                # 打印该item的完整内容
                                                print(f"  完整内容: {json.dumps(item, ensure_ascii=False, indent=2)}")
                                                
                                            # 检查是否有children字段
                                            if 'children' in item:
                                                children = item['children']
                                                print(f"  - children类型: {type(children).__name__}")
                                                if isinstance(children, list):
                                                    print(f"    子children数量: {len(children)}")
                                                    # 检查children中是否有label等于Skip or not
                                                    for j, child in enumerate(children[:3], 1):
                                                        if isinstance(child, dict):
                                                            if child.get('label') == 'Skip or not':
                                                                print(f"    \n    找到Skip or not! 在子项 {j}")
                                                                print(f"    完整内容: {json.dumps(child, ensure_ascii=False, indent=2)}")
                                                            print(f"    子项 {j} label: {child.get('label', 'N/A')}")
                                
                                # 也处理labelConfig是字典的情况（兼容之前的代码）
                                elif isinstance(label_config, dict):
                                    print(f"7. labelConfig字典键: {list(label_config.keys())}")
                                    print(f"\n=== labelConfig完整结构 ===")
                                    print(json.dumps(label_config, ensure_ascii=False, indent=2))
                                    
                                    # 检查是否有attributes或其他可能包含children的字段
                                    if 'attributes' in label_config:
                                        attributes = label_config['attributes']
                                        print(f"\n8. attributes类型: {type(attributes).__name__}")
                                        if isinstance(attributes, dict):
                                            print(f"9. attributes字典键: {list(attributes.keys())}")
                                            
                                            if 'children' in attributes:
                                                children = attributes['children']
                                                print(f"10. children类型: {type(children).__name__}")
                                                
                                                if isinstance(children, list):
                                                    print(f"11. children列表长度: {len(children)}")
                                                    print(f"\n=== children列表内容预览 ===")
                                                    for i, child in enumerate(children[:3], 1):
                                                        if isinstance(child, dict):
                                                            print(f"\n子项 {i}:")
                                                            if 'label' in child:
                                                                print(f"  - label: {child['label']}")
                                                            if 'children' in child:
                                                                print(f"  - children: {type(child['children']).__name__}")
                            else:
                                print("\nsetting中没有labelConfig字段")
                    else:
                        print("\ndata中没有setting字段")
        except json.JSONDecodeError:
            print(f"响应内容不是有效的JSON: {response.text[:200]}...")
            # 打印完整的响应文本以便调试
            print(f"\n完整响应文本: {response.text}")
            return ""
        
        # 根据response_data.json中的实际结构，从data.setting.labelConfig[0].attributes中查找Skip or not
        try:
            if 'data' in response_data and isinstance(response_data['data'], dict):
                data = response_data['data']
                print(f"data结构: {list(data.keys())}")
                
                if 'setting' in data and isinstance(data['setting'], dict):
                    setting = data['setting']
                    print(f"setting结构: {list(setting.keys())}")
                    
                    if 'labelConfig' in setting and isinstance(setting['labelConfig'], list):
                        label_config = setting['labelConfig']
                        print(f"labelConfig类型: list, 长度: {len(label_config)}")
                        
                        # 初始化返回值
                        skip_or_not_value = ""
                        applicable_value = ""
                        
                        # 遍历labelConfig列表
                        for config_item in label_config:
                            if isinstance(config_item, dict) and 'attributes' in config_item:
                                attributes = config_item['attributes']
                                print(f"发现attributes字段，类型: {type(attributes).__name__}")
                                
                                # attributes可能是字典或列表
                                if isinstance(attributes, list):
                                    print(f"attributes是列表，长度: {len(attributes)}")
                                    # 遍历attributes列表查找Skip or not
                                    for attr in attributes:
                                        if isinstance(attr, dict):
                                            # 查找Skip or not
                                            if attr.get('label') == 'Skip or not':
                                                print(f"找到Skip or not配置项!")
                                                print(f"配置项完整信息: {json.dumps(attr, ensure_ascii=False, indent=2)}")
                                                
                                                # 根据需求，只获取第一个children的label值（即第45行的"No"）
                                                if 'children' in attr and isinstance(attr['children'], list) and attr['children']:
                                                    first_child = attr['children'][0]
                                                    if isinstance(first_child, dict) and 'label' in first_child:
                                                        skip_or_not_value = first_child['label']
                                                        print(f"成功提取Skip or not配置值: {skip_or_not_value}")
                                                
                                                # 从Skip or not的children中查找Applicable值
                                                # 路径: Skip or not -> No -> All numbers covered: Applicable or not -> Applicable
                                                if 'children' in attr and isinstance(attr['children'], list):
                                                    for no_child in attr['children']:
                                                        if isinstance(no_child, dict) and no_child.get('label') == 'No':
                                                            if 'children' in no_child and isinstance(no_child['children'], list):
                                                                for applicable_parent in no_child['children']:
                                                                    if isinstance(applicable_parent, dict) and 'label' in applicable_parent:
                                                                        if 'Applicable or not' in applicable_parent['label']:
                                                                            if 'children' in applicable_parent and isinstance(applicable_parent['children'], list):
                                                                                for applicable_child in applicable_parent['children']:
                                                                                    if isinstance(applicable_child, dict) and applicable_child.get('label') == 'Applicable':
                                                                                        applicable_value = applicable_child.get('label')
                                                                                        print(f"成功提取Applicable配置值: {applicable_value}")
                                elif isinstance(attributes, dict):
                                    # 也处理attributes是字典的情况
                                    print(f"attributes是字典，键: {list(attributes.keys())}")
                                    # 查找label为Skip or not的字段
                                    if attributes.get('label') == 'Skip or not':
                                        print(f"找到Skip or not配置项!")
                                        if 'children' in attributes and isinstance(attributes['children'], list):
                                            for child in attributes['children']:
                                                if isinstance(child, dict) and 'label' in child:
                                                    skip_or_not_value = child['label']
                                                    print(f"成功提取Skip or not配置值: {skip_or_not_value}")
                                                    return skip_or_not_value
                        
                        # 返回提取到的值，优先返回Skip or not
                        if skip_or_not_value:
                            return skip_or_not_value
                        return applicable_value
            print("未找到Skip or not配置")
            return ""
        except Exception as e:
            print(f"解析Skip or not配置时出错: {str(e)}")
            return ""
            
    except Exception as e:
        print(f"获取任务信息失败: {str(e)}")
        import traceback
        print(f"错误详情: {traceback.format_exc()}")
        return ""

def get_item_detail(task_id, node_id, item_ids, headers=None):
    """
    调用get-item-detail接口获取项目详细信息，提取labeler相关信息、checkStatus和nodeId
    
    Args:
        task_id (str): 任务ID
        node_id (str): 节点ID
        item_ids (list): 项目ID列表
        headers (dict, optional): 自定义请求头
    
    Returns:
        tuple: (labeler_info, check_status_info, node_id_info)，包含item_id对应的标注人、审核状态和节点信息
    """
    # 使用新域名的URL
    url = "https://app.abaka.ai/api/v2/item/get-item-detail"
    
    if headers is None:
        # 使用默认批次ID或从item_ids中获取第一个作为referer中的batchId
        referer_batch_id = item_ids[0] if item_ids else "68d28091dd785cd49b388c61"
        headers = get_default_headers(task_id=task_id, batch_id=referer_batch_id)
    
    # 打印请求头信息（隐藏敏感信息）
    masked_headers = headers.copy()
    if 'access-token' in masked_headers:
        token = masked_headers['access-token']
        masked_headers['access-token'] = f"{token[:10]}****{token[-4:]}"
    print(f"\nget_item_detail - 请求头(部分): {dict(list(masked_headers.items())[:3])}")
    print(f"请求参数: taskId={task_id}, nodeId={node_id}, item_ids数量={len(item_ids)}")
    
    payload = {
        "taskId": task_id,
        "ai": False,
        "nodeId": node_id,
        "itemIds": item_ids
    }
    
    try:
        print(f"\n正在获取 {len(item_ids)} 个项目的详细信息...")
        # 添加verify=False以忽略SSL验证
        response = requests.post(url, headers=headers, json=payload, timeout=30, verify=False)
        print(f"响应状态码: {response.status_code}")
        
        # 如果返回401错误，尝试切换token并重新请求
        if response.status_code == 401:
            print("\n[错误] 认证失败 (401 Unauthorized)")
            print("尝试切换到备用token...")
            switch_token("alternative_token")
            # 使用新token重新构建请求头
            headers = get_default_headers(task_id=task_id, batch_id=referer_batch_id)
            print("使用新token重新请求...")
            # 添加verify=False以忽略SSL验证
            response = requests.post(url, headers=headers, json=payload, timeout=30, verify=False)
            print(f"新请求状态码: {response.status_code}")
        
        # 尝试解析响应内容
        try:
            response_data = response.json()
            print(f"响应内容(部分): {json.dumps(response_data)[:200]}...")
        except json.JSONDecodeError:
            print(f"响应内容不是有效的JSON: {response.text[:200]}...")
            return {}, {}, {}
        
        # 初始化所有item_ids的默认值
        labeler_info = {item_id: "-" for item_id in item_ids}
        check_status_info = {item_id: "-" for item_id in item_ids}
        node_id_info = {item_id: "-" for item_id in item_ids}
        
        if 'data' in response_data and isinstance(response_data['data'], dict):
            print("正在提取labeler信息和checkStatus...")
            
            for item_id, item_detail in response_data['data'].items():
                username = "-"
                check_status = "-"
                
                # 尝试从labeler字段获取username
                if isinstance(item_detail, dict):
                    # 获取标注人信息 - 确保当labeler不存在或username不存在时显示"-"
                    if 'labeler' in item_detail and isinstance(item_detail['labeler'], dict):
                        username = item_detail['labeler'].get('username', '-')
                    # 如果labeler字段不存在或不是字典，则保持默认值"-"
                    
                    # 获取checkStatus信息
                    if 'checkStatus' in item_detail:
                        check_status = item_detail.get('checkStatus', '-')
                    # 或者检查processStatus中的check相关字段
                    elif 'processStatus' in item_detail and isinstance(item_detail['processStatus'], dict):
                        check_status = item_detail['processStatus'].get('checkStatus', '-')
                    
                    # 获取nodeId信息
                    node_id_value = '-'  
                    if 'nodeId' in item_detail:
                        node_id_value = item_detail.get('nodeId', '-')
                    # 或者检查processStatus中的nodeId
                    elif 'processStatus' in item_detail and isinstance(item_detail['processStatus'], dict):
                        node_id_value = item_detail['processStatus'].get('nodeId', '-')
                
                labeler_info[item_id] = username
                check_status_info[item_id] = check_status
                node_id_info[item_id] = node_id_value
            
            print(f"成功提取 {len(labeler_info)} 条labeler信息、{len(check_status_info)} 条checkStatus信息和 {len(node_id_info)} 条nodeId信息")
        else:
            print("响应数据格式不符合预期")
            if response_data:
                print(f"响应结构: {list(response_data.keys())}")
                
        return labeler_info, check_status_info, node_id_info
        
    except Exception as e:
        print(f"获取项目详情失败: {str(e)}")
        import traceback
        print(f"错误详情: {traceback.format_exc()}")
        return {}, {}, {}

def extract_id_and_frames(task_id, node_id, item_ids, ai=False):
    """
    调用find-items-file接口，提取item_id和frames字段
    
    Args:
        task_id (str): 任务ID
        node_id (str): 节点ID
        item_ids (list): 项目ID列表
        ai (bool): 是否使用AI
    
    Returns:
        list: 包含id和frames的列表
    """
    url = "https://app.abaka.ai/api/v2/item/find-items-file"
    
    # 使用默认批次ID或从item_ids中获取第一个作为referer中的batchId
    referer_batch_id = item_ids[0] if item_ids else "68d28091dd785cd49b388c61"
    headers = get_default_headers(task_id=task_id, batch_id=referer_batch_id)
    
    # 打印请求头信息（隐藏敏感信息）
    masked_headers = headers.copy()
    if 'access-token' in masked_headers:
        token = masked_headers['access-token']
        masked_headers['access-token'] = f"{token[:10]}****{token[-4:]}"
    print(f"\nextract_id_and_frames - 请求头(部分): {dict(list(masked_headers.items())[:3])}")
    print(f"请求参数: taskId={task_id}, nodeId={node_id}, item_ids数量={len(item_ids)}, ai={ai}")
    
    payload = {
        "taskId": task_id,
        "ai": ai,
        "nodeId": node_id,
        "itemIds": item_ids
    }
    
    try:
        print(f"\n正在获取 {len(item_ids)} 个项目的文件信息...")
        response = requests.post(url, headers=headers, json=payload, timeout=30)
        print(f"响应状态码: {response.status_code}")
        
        # 如果返回401错误，尝试切换token并重新请求
        if response.status_code == 401:
            print("\n[错误] 认证失败 (401 Unauthorized)")
            print("尝试切换到备用token...")
            switch_token("alternative_token")
            # 使用新token重新构建请求头
            headers = get_default_headers(task_id=task_id, batch_id=referer_batch_id)
            print("使用新token重新请求...")
            response = requests.post(url, headers=headers, json=payload, timeout=30)
            print(f"新请求状态码: {response.status_code}")
        
        # 尝试解析响应内容
        try:
            response_data = response.json()
            print(f"响应内容(部分): {json.dumps(response_data)[:200]}...")
        except json.JSONDecodeError:
            print(f"响应内容不是有效的JSON: {response.text[:200]}...")
            return []
        
        simplified_data = []
        
        if "data" in response_data and isinstance(response_data["data"], dict):
            print("正在提取ID和frames信息...")
            
            for item_id, item_info in response_data["data"].items():
                frames = item_info.get("frames", 0)
                simplified_data.append({
                    "id": item_id,
                    "frames": frames
                })
            
            print(f"成功提取 {len(simplified_data)} 条记录")
        else:
            print("响应数据格式不符合预期")
            if response_data:
                print(f"响应结构: {list(response_data.keys())}")
            
        return simplified_data
        
    except Exception as e:
        print(f"提取文件信息失败: {str(e)}")
        import traceback
        print(f"错误详情: {traceback.format_exc()}")
        return []

def convert_to_beijing_time(utc_time_str):
    """
    将UTC时间字符串转换为北京时间
    
    Args:
        utc_time_str (str): UTC时间字符串，格式如 "2025-09-23T11:13:37.488Z"
    
    Returns:
        str: 北京时间字符串，格式为 "2025-09-23 19:13:37"
    """
    if not utc_time_str or not isinstance(utc_time_str, str):
        return utc_time_str
    
    try:
        # 移除可能的毫秒部分
        if '.' in utc_time_str:
            utc_time_str = utc_time_str.split('.')[0] + 'Z'
        
        # 解析UTC时间
        utc_time = datetime.strptime(utc_time_str, "%Y-%m-%dT%H:%M:%SZ")
        
        # 转换为北京时间（UTC+8）
        beijing_time = utc_time + timedelta(hours=8)
        
        # 格式化为更友好的字符串
        return beijing_time.strftime("%Y-%m-%d %H:%M:%S")
    except Exception:
        # 如果解析失败，返回原始字符串
        return utc_time_str

def get_skip_or_not_from_file(file_path, item_ids):
    """
    从find_labels_response.json文件中提取指定item_ids的Skip or not值
    
    Args:
        file_path (str): JSON文件路径
        item_ids (list): 需要查询的项目ID列表
    
    Returns:
        dict: {item_id: skip_or_not_value} 格式的字典
    """
    import json
    skip_or_not_dict = {item_id: "" for item_id in item_ids}
    
    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            response_data = json.load(f)
        
        # 检查是否存在data数组
        if isinstance(response_data, dict) and 'data' in response_data and isinstance(response_data['data'], list):
            # 遍历data数组中的每个元素
            for item in response_data['data']:
                if isinstance(item, dict) and 'itemId' in item and 'data' in item:
                    item_id = item['itemId']
                    # 只处理在item_ids列表中的项目
                    if item_id in skip_or_not_dict:
                        data_obj = item['data']
                        # 检查data中是否有label为"check_Skip or not"的字段
                        if isinstance(data_obj, dict) and data_obj.get('label') == 'check_Skip or not':
                            # 提取value值
                            skip_or_not_value = data_obj.get('value', '')
                            skip_or_not_dict[item_id] = skip_or_not_value
                            print(f"从文件中成功提取 {item_id} 的Skip or not值: {skip_or_not_value}")
        
        print(f"总共从文件中提取了 {sum(1 for v in skip_or_not_dict.values() if v)} 个项目的Skip or not值")
        return skip_or_not_dict
    except Exception as e:
        print(f"从文件提取Skip or not值时出错: {str(e)}")
        return skip_or_not_dict

def correlate_and_display(item_list_data, frames_data, labeler_info=None, check_status_info=None, node_id_info=None, labels_info=None, skip_or_not_info=None, export_to_excel=True):
    """
    关联并显示项目列表、frames信息、labeler信息和checkStatus
    
    Args:
        item_list_data (list): 从get_item_list、get_item_abandon_list获取的数据列表（已在main中处理跳过状态）
        frames_data (list): 从extract_id_and_frames获取的数据列表
        labeler_info (dict, optional): 从get_item_detail获取的labeler信息
        check_status_info (dict, optional): 从get_item_detail获取的checkStatus信息
        node_id_info (dict, optional): 从get_item_detail获取的nodeId信息
        labels_info (dict, optional): 从get_find_labels_batch获取的标签信息
        skip_or_not_info (dict, optional): 从文件获取的{item_id: skip_or_not_value}格式的字典
        export_to_excel (bool): 是否导出到Excel表格，默认为True
    """
    if not item_list_data:
        print("没有项目数据可显示")
        return
    
    # 创建frames数据的字典，方便快速查找
    frames_dict = {item["id"]: item["frames"] for item in frames_data} if frames_data else {}
    
    print("\n===== 项目信息、Frames与标注人关联结果 =====")
    
    # 根据是否有labeler信息决定表头
    if labeler_info:
        print(f"{'序号':<6}{'ID':<32}{'domainId':<10}{'operateTime':<20}{'Frames':<8}{'标注状态':<8}{'标注人':<10}")
        print("-" * 104)
    else:
        print(f"{'序号':<6}{'ID':<32}{'domainId':<10}{'operateTime':<20}{'Frames':<8}{'标注状态':<8}")
        print("-" * 94)
    
    # 统计信息
    matched_count = 0
    unmatched_count = 0
    labeled_count = 0
    annotated_count = 0  # 已标注数量
    annotating_count = 0  # 标注中数量
    abandoned_count = 0  # 已放弃数量
    skipped_count = 0  # 已跳过数量
    
    # 准备Excel导出数据
    excel_data = []
    
    # 遍历项目列表数据，关联frames和labeler信息
    for idx, item in enumerate(item_list_data, 1):
        item_id = item["_id"]
        frames = frames_dict.get(item_id, "未找到")
        username = labeler_info.get(item_id, "-") if labeler_info else "-"
        check_status = check_status_info.get(item_id, "-") if check_status_info else "-"
        node_id_value = node_id_info.get(item_id, "-") if node_id_info else "-"
        domain_id = item.get("domainId", "")
        
        # 直接使用项目中的annotationStatus字段（已在main函数中处理了跳过状态）
        annotation_status = item.get("annotationStatus", "未知")
        
        # 去除可能的换行符
        if isinstance(username, str):
            username = username.replace('\n', '')
        
        # 转换为北京时间
        beijing_time = convert_to_beijing_time(item['operateTime'])
        
        # 获取Modified instruction数据
        modified_instruction_1 = ""
        modified_instruction_2 = ""
        if labels_info and item_id in labels_info:
            # labels_info[item_id]现在是包含modified_instruction字段的字典
            item_labels = labels_info[item_id]
            if isinstance(item_labels, dict):
                modified_instruction_1 = item_labels.get('modified_instruction_1', '')
                modified_instruction_2 = item_labels.get('modified_instruction_2', '')
        
        # 构建导出数据
        # 获取当前item_id对应的Skip or not值
        skip_or_not_value = ""
        if skip_or_not_info and isinstance(skip_or_not_info, dict):
            skip_or_not_value = skip_or_not_info.get(item_id, "")
        elif skip_or_not_info and isinstance(skip_or_not_info, str):
            # 保持向后兼容
            skip_or_not_value = skip_or_not_info
        
        row_data = {
            '序号': idx,
            'ID': item_id,
            'domainId': domain_id,
            'operateTime': beijing_time,  # 使用北京时间
            'Frames': frames,
            '标注状态': annotation_status,
            'checkStatus': check_status,
            'nodeId': node_id_value,
            'Modified instruction_1': modified_instruction_1,  # 添加Modified instruction列
            'Modified instruction_2': modified_instruction_2,
            'Skip or not': skip_or_not_value  # 添加Skip or not列
        }
        if labeler_info:
            row_data['标注人'] = username
        excel_data.append(row_data)
        
        if labeler_info:
            print(f"{idx:<6}{item_id:<32}{str(domain_id):<10}{beijing_time:<20}{str(frames):<8}{annotation_status:<8}{username:<10}")
        else:
            print(f"{idx:<6}{item_id:<32}{str(domain_id):<10}{beijing_time:<20}{str(frames):<8}{annotation_status:<8}")
        
        if frames != "未找到":
            matched_count += 1
        else:
            unmatched_count += 1
        
        if username != "-":
            labeled_count += 1
        
        # 根据标注状态统计
        if annotation_status == "已标注":
            annotated_count += 1
        elif annotation_status == "标注中":
            annotating_count += 1
        elif annotation_status == "已放弃":
            abandoned_count += 1
        elif annotation_status == "已跳过":
            skipped_count += 1
    
    # 打印统计信息
    if labeler_info:
        print("-" * 104)
        print(f"总计: {len(item_list_data)} 条项目记录")
        print(f"成功关联Frames: {matched_count} 条")
        print(f"未找到Frames匹配: {unmatched_count} 条")
        print(f"有标注人信息: {labeled_count} 条")
        print(f"无标注人信息: {len(item_list_data) - labeled_count} 条")
        print(f"已标注: {annotated_count} 条")
        print(f"标注中: {annotating_count} 条")
        print(f"已放弃: {abandoned_count} 条")
        print(f"已跳过: {skipped_count} 条")
    else:
        print("-" * 94)
        print(f"总计: {len(item_list_data)} 条项目记录")
        print(f"成功关联Frames: {matched_count} 条")
        print(f"未找到Frames匹配: {unmatched_count} 条")
        print(f"已标注: {annotated_count} 条")
        print(f"标注中: {annotating_count} 条")
        print(f"已放弃: {abandoned_count} 条")
        print(f"已跳过: {skipped_count} 条")
    
    # 导出到Excel
    if export_to_excel and excel_data:
        try:
            # 创建checkStatus映射字典
            check_status_mapping = {
                'APPROVED': '审核通过',
                'RE_APPROVED': '复审通过', 
                'REJECTED': '审核不通过',
                'SKIP': '已跳过',
                'UNCHECKED': '未审核',
                'CHECKING': '审核中',
                'UNRECHECKED': '未复审',
            }
            
            # 创建nodeId映射字典
            node_id_mapping = {
                '68ee06dd8b09f9d346b6d37b': '搁置',
                '68d255e332a0f5d164c7082e': '审核',
                '68d255e332a0f5d164c7082d': '标注内审',
                '68d9ee42fd32a29543c7e1a4': '标注内审2',
                '68d255e332a0f5d164c7082f': '验收',
                '68d255e332a0f5d164c7082c': '标注',
                '68da1d528bb1370c422d13ec': '标注内审2',
                '68d4aa85885469ceef9626e4': '标注',
                '68d4aa85885469ceef9626e5': '标注内审',
                '68e38ffb138a749868212d4c': '处理ed',
            }
            
            # 创建新的数据结构，只包含需要的列
            new_excel_data = []
            for item in excel_data:
                # 获取并转换checkStatus
                check_status_value = item.get('checkStatus', '-')
                # 如果在映射字典中存在，则转换为中文，否则保持原值
                converted_check_status = check_status_mapping.get(check_status_value, check_status_value)
                
                # 获取并转换nodeId
                node_id_value = item.get('nodeId', '-')
                # 如果在映射字典中存在，则转换为中文，否则保持原值
                converted_node_id = node_id_mapping.get(node_id_value, node_id_value)
                
                # 获取并格式化操作日期
                operate_time = item.get('operateTime', '-')
                if operate_time != '-':
                    # 确保是字符串
                    time_str = str(operate_time)
                    # 处理不同格式的时间字符串
                    if ' ' in time_str:  # 处理包含空格的格式，如"2023-10-15 12:34:56"
                        formatted_date = time_str.split(' ')[0]
                    elif 'T' in time_str:  # 处理ISO格式，如"2023-10-15T12:34:56Z"
                        formatted_date = time_str.split('T')[0]
                    elif len(time_str) >= 10:  # 处理其他可能的格式，只取前10个字符
                        formatted_date = time_str[:10]
                    else:
                        formatted_date = time_str
                else:
                    formatted_date = '-'  
                
                new_item = {
                    '标注人': item.get('标注人', '-'),
                    '空白列': '',
                    '条目号（domainId）': item.get('domainId', ''),
                    '操作日期': formatted_date,  # 确保只显示日期部分
                    '帧数(Frames)': item.get('Frames', ''),
                    '标注状态': item.get('标注状态', ''),
                    '当前审核状态': converted_check_status,
                    '节点类型': converted_node_id,
                    'Skip or not': item.get('Skip or not', ''),  # 添加Skip or not列
                    'Modified instruction_1': item.get('Modified instruction_1', ''),
                    'Modified instruction_2': item.get('Modified instruction_2', '')
                }
                new_excel_data.append(new_item)
            
            # 创建DataFrame
            df = pd.DataFrame(new_excel_data)
            
            # 生成文件名（使用当前时间）
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            filename = f"项目关联结果_{timestamp}.xlsx"
            
            # 保存到Excel
            with pd.ExcelWriter(filename, engine='openpyxl') as writer:
                df.to_excel(writer, index=False, sheet_name='项目数据')
                
                # 获取工作表
                worksheet = writer.sheets['项目数据']
                
                # 设置列宽
                worksheet.column_dimensions['A'].width = 12  # 标注人
                worksheet.column_dimensions['B'].width = 5   # 空白列
                worksheet.column_dimensions['C'].width = 15  # 条目号（domainId）
                worksheet.column_dimensions['D'].width = 12  # 操作日期
                worksheet.column_dimensions['E'].width = 12  # 帧数(Frames)
                worksheet.column_dimensions['F'].width = 12  # 标注状态
                worksheet.column_dimensions['G'].width = 12  # 当前审核状态
                worksheet.column_dimensions['H'].width = 12  # 节点类型
                worksheet.column_dimensions['I'].width = 20  # Skip or not
                worksheet.column_dimensions['J'].width = 30  # Modified instruction_1
                worksheet.column_dimensions['K'].width = 30  # Modified instruction_2
            
            print(f"\n已成功导出数据到Excel文件: {filename}")
        except Exception as e:
            print(f"导出Excel时出错: {str(e)}")
            # 添加调试信息
            if len(excel_data) > 0:
                print(f"前5行数据示例: {excel_data[:5]}")

def main():
    """
    主函数，整合API调用并关联数据
    """
    print("===== API整合工具 - 项目信息、Frames与标注人关联 =====\n")
    print("注意：请确保输入正确的task_id、node_id和package_ids，以便获取完整的标注人、审核状态和节点类型信息\n")
    
    # 获取默认值
    default_task_id = "68ef7506b89637431efa70dc"
    default_node_id = "68ef7506b89637431efa70e1"
    default_package_ids = "68f1e89b8fc939d1af1b726f"
    default_page_size = 1000
    
    # 移除命令行参数解析，直接使用交互式输入
    print("交互式配置模式：")
    print("每个参数都需要确认，使用默认值时请输入 y 确认")
    print("====================================\n")
    
    # 辅助函数：获取参数并确认
    def get_and_confirm_param(param_name, default_value, param_type='str'):
        while True:
            print(f"当前{param_name}默认值: {default_value}")
            user_input = input(f"请输入新的{param_name}（直接回车使用默认值）: ").strip()
            
            if user_input:
                # 用户输入了新值
                if param_type == 'int':
                    try:
                        value = int(user_input)
                        print(f"\n已确认{param_name}: {value}")
                        return value
                    except ValueError:
                        print(f"错误：请输入有效的数字！\n")
                        continue
                else:
                    print(f"\n已确认{param_name}: {user_input}")
                    return user_input
            else:
                # 用户选择使用默认值，需要确认
                confirm = input(f"确定使用默认{param_name}值 '{default_value}' 吗？(y/n): ").strip().lower()
                if confirm == 'y':
                    print(f"\n已确认使用默认{param_name}: {default_value}")
                    return default_value
                else:
                    print("请重新输入...\n")
    
    # 获取并确认各参数
    print("配置 task_id:")
    task_id = get_and_confirm_param("task_id", default_task_id)
    print("====================================\n")
    
    print("配置 node_id:")
    node_id = get_and_confirm_param("node_id", default_node_id)
    print("====================================\n")
    
    print("配置 package_ids:")
    print("提示：多个ID请用逗号分隔")
    package_ids_input = get_and_confirm_param("package_ids", default_package_ids)
    print("====================================\n")
    
    print("配置 page_size:")
    page_size = get_and_confirm_param("page_size", default_page_size, 'int')
    print("====================================\n")
    
    # access_token 处理（可选）
    print("配置 access_token（可选）:")
    print("提示：直接回车使用配置文件中的token")
    access_token = input("access_token: ").strip()
    
    # 处理批次ID，支持逗号分隔的多个ID
    package_ids = [pid.strip() for pid in package_ids_input.split(',')]
    
    # 如果提供了访问令牌，则更新配置
    if access_token:
        # 确认更新access_token
        confirm_token = input(f"确定要更新access_token吗？(y/n): ").strip().lower()
        if confirm_token == 'y':
            update_access_token(access_token)
            print(f"\n已更新access_token")
        else:
            print("\n保持原有access_token")
    else:
        print("\n使用配置文件中的access_token")
    print("====================================\n")
    
    # 如果提供了访问令牌，则更新配置
    if access_token:
        update_access_token(access_token)
    
    # 打印当前配置信息
    print_config_info()
    
    page_num = 1
    
    print(f"\n配置参数:")
    print(f"  task_id: {task_id}")
    print(f"  node_id: {node_id}")
    print(f"  package_ids: {package_ids}")
    print(f"  page_num: {page_num}")
    print(f"  page_size: {page_size}")
    
    # 步骤1: 获取项目列表数据
    print("\n===== 步骤1: 获取项目列表 =====")
    print(f"正在使用配置: task_id={task_id}, node_id={node_id}, package_ids={package_ids}")
    print("注意：确保这些ID是正确的，否则可能无法获取到标注人、审核状态和节点类型信息")
    
    # 获取普通项目列表
    print("获取普通项目列表...")
    normal_item_result = get_item_list(task_id, node_id, package_ids, page_num, page_size)
    
    if not normal_item_result["success"]:
        print(f"获取普通项目列表失败: {normal_item_result['message']}")
        
        # 尝试使用todo_list_extractor作为备选方案
        try:
            print("\n尝试使用get-item-todo-list接口作为备选方案...")
            import todo_list_extractor
            todo_result = todo_list_extractor.get_item_todo_list(task_id, node_id, package_ids)
            
            if todo_result["success"]:
                print(f"成功从todo-list获取到数据，共 {len(todo_result['data'])} 条记录")
                normal_item_list = []
                for item in todo_result['data']:
                    # 获取operateTime或createdAt，只保留日期部分
                    time_value = item.get('operateTime', item.get('createdAt', 'N/A'))
                    # 如果时间值不为N/A，尝试提取日期部分
                    if time_value != 'N/A':
                        # 确保时间值是字符串
                        time_str = str(time_value)
                        # 处理不同格式的时间字符串
                        if ' ' in time_str:  # 处理包含空格的格式，如"2023-10-15 12:34:56"
                            formatted_date = time_str.split(' ')[0]
                        elif 'T' in time_str:  # 处理ISO格式，如"2023-10-15T12:34:56Z"
                            formatted_date = time_str.split('T')[0]
                        elif len(time_str) >= 10:  # 处理其他可能的格式，只取前10个字符
                            formatted_date = time_str[:10]
                        else:
                            formatted_date = time_str
                    else:
                        formatted_date = 'N/A'
                    
                    normal_item_list.append({
                        "_id": item.get('_id', 'N/A'),
                        "domainId": item.get('domainId', 'N/A'),
                        "operateTime": formatted_date,  # 只保留日期部分
                        "annotationStatus": "未知"  # 添加标注状态字段
                    })
                print(f"已转换 {len(normal_item_list)} 条记录用于后续处理")
            else:
                print(f"todo-list接口也调用失败: {todo_result['message']}")
                print("\n程序执行失败。建议检查以下几点:")
                print("1. 确认access-token是否有效")
                print("2. 确认task_id和node_id是否正确")
                print("3. 确认网络连接是否正常")
                return
        except Exception as e:
            print(f"尝试使用备选方案失败: {str(e)}")
            return
    else:
        normal_item_list = normal_item_result["data"]
        print(f"从普通项目列表中提取到 {len(normal_item_list)} 条记录")
    
    # 获取废弃项目列表
    print("\n获取废弃项目列表...")
    abandon_item_result = get_item_abandon_list(task_id, node_id, package_ids, page_num, page_size)
    abandon_item_list = []
    
    if abandon_item_result["success"]:
        abandon_item_list = abandon_item_result["data"]
        # 为废弃项目设置标注状态为"已放弃"
        for item in abandon_item_list:
            item["annotationStatus"] = "已放弃"
        print(f"从废弃项目列表中提取到 {len(abandon_item_list)} 条记录")
    else:
        print(f"获取废弃项目列表失败: {abandon_item_result['message']}")
        print("将继续使用普通项目数据...")
    
    # 合并普通项目和废弃项目
    item_list_data = normal_item_list + abandon_item_list
    
    # 创建项目ID到索引的映射，方便快速查找
    item_id_map = {item["_id"]: idx for idx, item in enumerate(item_list_data)}
    
    # 获取跳过项目列表
    print("\n获取跳过项目列表...")
    skip_item_result = get_item_skip_list(task_id, node_id, package_ids, page_num, page_size)
    
    # 遍历被跳过的项目，在现有项目中查找并修改状态
    skipped_count = 0
    if skip_item_result["success"]:
        skip_items = skip_item_result["data"]
        print(f"获取到 {len(skip_items)} 条被跳过的项目")
        
        for skip_item in skip_items:
            skip_item_id = skip_item.get("_id")
            if skip_item_id in item_id_map:
                # 找到匹配的项目，修改其标注状态为"已跳过"
                item_list_data[item_id_map[skip_item_id]]["annotationStatus"] = "已跳过"
                skipped_count += 1
        print(f"成功将 {skipped_count} 条项目状态修改为'已跳过'")
    else:
        print(f"获取跳过项目列表失败: {skip_item_result['message']}")
        print("将继续使用已有数据...")
    
    # 根据domainId对数据进行排序
    item_list_data.sort(key=lambda x: str(x.get('domainId', '')))
    print(f"已按domainId排序完成")
    
    print(f"\n总计: {len(item_list_data)} 条项目记录 (普通项目: {len(normal_item_list)}, 废弃项目: {len(abandon_item_list)}, 已标记为跳过: {skipped_count})")
    
    if not item_list_data:
        print("没有获取到任何项目数据，程序无法继续")
        return
    
    # 步骤2: 提取所有_id作为参数
    item_ids = [item["_id"] for item in item_list_data]
    if not item_ids:
        print("没有找到有效的item_ids，无法继续")
        return
    print(f"提取到 {len(item_ids)} 个项目ID")
    
    # 步骤3: 调用extract_id_and_frames获取frames信息
    print("\n===== 步骤2: 获取文件信息 =====")
    frames_data = extract_id_and_frames(task_id, node_id, item_ids)
    
    # 步骤4: 调用get_item_detail获取labeler信息、checkStatus和nodeId信息
    print("\n===== 步骤3: 获取标注人信息、checkStatus和节点信息 =====")
    labeler_info, check_status_info, node_id_info = get_item_detail(task_id, node_id, item_ids)
    
    # 步骤5: 获取标签信息，统计区域删除
    print("\n===== 步骤4: 获取标签信息，统计区域删除 =====")
    labels_info = get_find_labels_batch(task_id, item_ids)
    
    # 步骤6: 从labels_info中提取Skip or not信息
    print("\n===== 步骤5: 从标签信息中提取Skip or not信息 =====")
    skip_or_not_info = {item_id: data.get("skip_or_not", "-") for item_id, data in labels_info.items()}
    print(f"成功提取 {len(skip_or_not_info)} 个项目的Skip or not信息")
    
    # 步骤7: 关联并显示结果
    print("\n===== 步骤6: 关联并显示结果 =====")
    correlate_and_display(item_list_data, frames_data, labeler_info, check_status_info, node_id_info, labels_info, skip_or_not_info, export_to_excel=True)
    
    print("\n===== 程序执行完成 =====")

if __name__ == "__main__":
    main()